package dfsketches.gr3d;

public class SolidRenderer extends Renderer {
	
	public SolidRenderer(int[] b, int w, int h) {
		bitmap = b;
		this.h = h;
		this.w = w;
		zbuffer = new float[h*w];
	}
	
	private void clearZBuffer() {
		final int sz = w*h;
		for (int i = 0; i < sz; i++) {
			zbuffer[i] = 0f;
		}
	}
	
	private void clearBitmap() {
		final int sz = w*h;
		for (int i = 0; i < sz; i++) {
			zbuffer[i] = 0xFFFFFFFF;
		}
	}

	public void start() {
		clearZBuffer();
		clearBitmap();
	}
	
	private void renderTrianglePart(int color, int start, int end,
			float s_x1, float s_y1, float s_z1, float s_px1, float s_py1,
			float s_x2, float s_y2, float s_z2, float s_px2, float s_py2,
			float f_x1, float f_y1, float f_z1, float f_px1, float f_py1,
			float f_x2, float f_y2, float f_z2, float f_px2, float f_py2) {
		final float dpy_s = s_py2 - s_py1;
		final float dz_s = s_z2 - s_z1;
		final float zk_s = dz_s / dpy_s;
		float z_s = s_z1 + zk_s * (start - s_py1);

		final float dpy_f = f_py2 - f_py1;
		final float dz_f = f_z2 - f_z1;
		final float zk_f = dz_f / dpy_f;
		float z_f = f_z1 + zk_f * (start - f_py1);
		
		final float pk_s = (s_px2 - s_px1) / (s_py2 - s_py1);
		float px_s = s_px1 + pk_s * (start - s_py1);

		final float pk_f = (f_px2 - f_px1) / (f_py2 - f_py1);
		float px_f = f_px1 + pk_f * (start - f_py1);
		
		int pos = start * w;
		
		for (int y = start; y < end; y++) {
			float dx = px_f - px_s; 
			float dz = z_f - z_s;
			float kz = dz / dx;
						
			float z = z_s;
			int px_s_i = (int)px_s;
			if (px_s_i < 0) {
				px_s_i = 0;
				z += -px_s * kz;
			}
			px_s_i += pos;
			int px_f_i = min((int)px_f, w) + pos;
			for (int x = px_s_i; x < px_f_i; x++) {
				if (z > zbuffer[x]) {
					zbuffer[x] = z;
					bitmap[x] = color;
				}
				z += kz;
			}
			
			px_s += pk_s;
			px_f += pk_f;
			
			z_s += zk_s;
			z_f += zk_f;
			
			pos += w;
		}
	}
		
	public void render(Triangle t) {
		Triangle tri = sortTriangle(t);
		
		int color = tri.color;
		
		Vertex a = tri.a;
		Vertex b = tri.b;
		Vertex c = tri.c;
		
		float aX = a.px;
		float aY = a.py;
		float bX = b.px;
		float bY = b.py;
		float cX = c.px;
		float cY = c.py;
		
		boolean orient = (((bX - aX) * (cY - aY)) >= ((cX - aX) * (bY - aY)));
		//false - АВС против часовой
		
		int start_y = max((int)aY, 0);
		int med_y = min(max((int)bY, 0), h);
		int end_y = min((int)cY, h);

		if (orient) {
			if (start_y != med_y)
				renderTrianglePart(color, start_y, med_y,
						a.x, a.y, 1f/a.z, a.px, a.py,
						c.x, c.y, 1f/c.z, c.px, c.py,
						a.x, a.y, 1f/a.z, a.px, a.py,
						b.x, b.y, 1f/b.z, b.px, b.py
						);
			if (med_y != end_y)
				renderTrianglePart(color, med_y, end_y,
						a.x, a.y, 1f/a.z, a.px, a.py,
						c.x, c.y, 1f/c.z, c.px, c.py,
						b.x, b.y, 1f/b.z, b.px, b.py,
						c.x, c.y, 1f/c.z, c.px, c.py
						);
		} else {
			if (start_y != med_y)
				renderTrianglePart(color, start_y, med_y,
						a.x, a.y, 1f/a.z, a.px, a.py,
						b.x, b.y, 1f/b.z, b.px, b.py,
						a.x, a.y, 1f/a.z, a.px, a.py,
						c.x, c.y, 1f/c.z, c.px, c.py
						);
			if (med_y != end_y)
				renderTrianglePart(color, med_y, end_y,
						b.x, b.y, 1f/b.z, b.px, b.py,
						c.x, c.y, 1f/c.z, c.px, c.py,
						a.x, a.y, 1f/a.z, a.px, a.py,
						c.x, c.y, 1f/c.z, c.px, c.py
						);
		}
	}
	
	private int min(int a, int b) {
		return (a < b) ? a : b;
	}
	
	private int max(int a, int b) {
		return (a > b) ? a : b;
	}
	
	int[] bitmap;
	float[] zbuffer;
	int w;
	int h;
	
}